/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jsf;

import entidades.Rol;
import entidades.Usuario;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.faces.FacesException;import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;
import javax.faces.validator.ValidatorException;
import javax.servlet.http.HttpSession;
import jpa.UsuarioJpaController;
import jsf.util.JsfUtil;
import jpa.exceptions.NonexistentEntityException;
import jsf.util.AuthenticationPhaseListener;
import jsf.util.SHA1;

/**
 *
 * @author dvasquez
 */
public class UsuarioController {

    public UsuarioController() {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        jpaController = (UsuarioJpaController) facesContext.getApplication().getELResolver().getValue(facesContext.getELContext(), null, "usuarioJpa");
        converter = new UsuarioConverter();
    }
    private Usuario usuario = null;
    private Usuario usuarioLogin = new Usuario();
    private Usuario usuarioClave = new Usuario();
    private Usuario usuarioSearch = new Usuario();
    private List<Usuario> usuarioItems = null, usuarioTotal = null;
    private UsuarioJpaController jpaController = null;
    private UsuarioConverter converter = null;
    private Boolean mostrarClave = false;
    private Boolean crearInforme = false;
    private UIViewRoot vr;


    public SelectItem[] getUsuarioItemsAvailableSelectMany() {
        return JsfUtil.getSelectItems(jpaController.findUsuarioEntities(), false);
    }

    public SelectItem[] getUsuarioItemsAvailableSelectOne() {
        return JsfUtil.getSelectItems(jpaController.findUsuarioEntities(), true);
    }

    public Usuario getUsuario() {
        if (usuario == null) {
            usuario = (Usuario) JsfUtil.getObjectFromRequestParameter("jsfcrud.currentUsuario", converter, null);
        }
        if (usuario == null) {
            usuario = new Usuario();
        }
        return usuario;
    }

    public Boolean getMostrarClave() {
        return mostrarClave;
    }

    public void setMostrarClave(Boolean mostrarClave) {
        this.mostrarClave = mostrarClave;
    }

    public Usuario getUsuarioClave() {
        if (usuarioClave == null) {
            usuarioClave = new Usuario();
        }
        return usuarioClave;
    }

    public void setUsuarioClave(Usuario usuarioClave) {
        this.usuarioClave = usuarioClave;
    }

    public Usuario getUsuarioLogin() {
        if (usuarioLogin == null) {
            usuarioLogin = new Usuario();
        }
        return usuarioLogin;
    }

    public Usuario getUsuarioSearch() {
        if (usuarioSearch == null) {
            usuarioSearch = new Usuario();
        }
        return usuarioSearch;
    }

    public Boolean getCrearInforme() {
        return crearInforme;
    }

    public void setCrearInforme(Boolean crearInforme) {
        this.crearInforme = crearInforme;
    }

    public String listSetup() {
        reset(true);
        crearInforme = false;
        return "usuario_list";
    }

    public String createSetup() {
        reset(false);
        usuario = new Usuario();
        usuarioClave = new Usuario();
        return "usuario_create";
    }

    public String searchSetup() {
        clearUsuario(usuarioSearch);
        crearInforme = false;
        return "usuario_search";
    }

    public String informeSetup() {
        crearInforme = true;
        return "usuario_search";
    }

    public String create() {
        Usuario user;
        if (!usuario.getClave().equals(usuarioLogin.getClave())) {
            JsfUtil.addErrorMessage("Las claves no coinciden.");
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "*", null);
            FacesContext.getCurrentInstance().addMessage("crearUsuarioForm:clave", fm);
            FacesContext.getCurrentInstance().addMessage("crearUsuarioForm:claveConf", fm);
            return null;
        }


        usuario.setDigito(Character.toUpperCase(usuario.getDigito()));
        if (!verificarRut(Integer.parseInt(usuario.getRut()), usuario.getDigito())) {
            JsfUtil.addErrorMessage("El digito verificador no es válido.");
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "*", null);
            FacesContext.getCurrentInstance().addMessage("crearUsuarioForm:digito", fm);
            return null;
        }
        try {
            jpaController.create(usuario);
            JsfUtil.addSuccessMessage("Usuario ingresado exitosamente.");
        } catch (Exception e) {
            JsfUtil.ensureAddErrorMessage(e, "Ocurrió un error al intentar ingresar el usuario. Por favor intente denuevo.");
            return null;
        }
        return listSetup();
    }

    public String detailSetup() {
        usuarioTotal = usuarioItems;
        return scalarSetup("usuario_detail");
    }

    public String backSetup(){
        usuarioItems = usuarioTotal;
        return "usuario_list";
    }

    public String editSetup() {
        usuarioTotal = usuarioItems;
        return scalarSetup("usuario_edit");
    }

    private String scalarSetup(String destination) {
        reset(false);
        usuario = (Usuario) JsfUtil.getObjectFromRequestParameter("jsfcrud.currentUsuario", converter, null);
        if (usuario == null) {
            String requestUsuarioString = JsfUtil.getRequestParameter("jsfcrud.currentUsuario");
            JsfUtil.addErrorMessage("The usuario with id " + requestUsuarioString + " no longer exists.");
            return relatedOrListOutcome();
        }
        if (destination.equals("usuario_edit")) {
            usuario.setClave("");
            usuarioLogin.setClave("");
        } else if (destination.equals("usuario_create")) {
            usuarioLogin.setClave("");
        }
        return destination;
    }

    public boolean verificarRut(int rut, char dv) {
        int m = 0, s = 1;
        for (; rut != 0; rut /= 10) {
            s = (s + rut % 10 * (9 - m++ % 6)) % 11;
        }
        return dv == (char) (s != 0 ? s + 47 : 75);
    }

    public String edit() {
        Usuario user = new Usuario();
        user = jpaController.findUsuario(usuario.getRut());
        if (!usuario.getClave().equals(usuarioLogin.getClave())) {
            JsfUtil.addErrorMessage("Las claves no coinciden.");
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "*", null);
            FacesContext.getCurrentInstance().addMessage("editarUsuarioForm:clave", fm);
            FacesContext.getCurrentInstance().addMessage("editarUsuarioForm:claveConf", fm);
            return null;
        }
        if (!this.getAdmin()) {
            if (!usuarioClave.getClave().equals(user.getClave())) {
                JsfUtil.addErrorMessage("La clave actual no es correcta.");
                FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "*", null);
                FacesContext.getCurrentInstance().addMessage("editarUsuarioForm:clave", fm);
                FacesContext.getCurrentInstance().addMessage("editarUsuarioForm:claveConf", fm);
                return null;
            }
        }
            

        String usuarioString = converter.getAsString(FacesContext.getCurrentInstance(), null, usuario);
        String currentUsuarioString = JsfUtil.getRequestParameter("jsfcrud.currentUsuario");
        if (usuarioString == null || usuarioString.length() == 0 || !usuarioString.equals(currentUsuarioString)) {
            String outcome = editSetup();
            if ("usuario_edit".equals(outcome)) {
                JsfUtil.addErrorMessage("No se pudo guardar los datos. Por favor, intente denuevo.");
            }
            return outcome;
        }
        try {
            jpaController.edit(usuario);
            JsfUtil.addSuccessMessage("Usuario actualizado exitosamente.");
        } catch (NonexistentEntityException ne) {
            JsfUtil.addErrorMessage(ne.getLocalizedMessage());
            return listSetup();
        } catch (Exception e) {
            JsfUtil.ensureAddErrorMessage(e, "Ocurrió un error al guardar los datos. Por favor, intente denuevo");
            return null;
        }
        return detailSetup();
    }

    public String destroy() {
        int admin = 0;
        String idAsString = JsfUtil.getRequestParameter("jsfcrud.currentUsuario");
        String id = idAsString;
        usuario = jpaController.findUsuario(id);
        List<Usuario> users;
        users = jpaController.findUsuarioEntities();
        for (Usuario u : users) {
            for (Rol r : u.getRolCollection()) {
                if (r.getNombre().equals("Administrador")) {
                    admin++;
                }
            }
        }
        if (admin < 2) {
            JsfUtil.addErrorMessage("No puede eliminar a todos los usuarios con rol Administrador.");
            return null;
        }
        try {
            jpaController.destroy(id);
            JsfUtil.addSuccessMessage("El usuario \"" + usuario.getPrimerNombre() + " " + usuario.getApellidoPaterno() + " " + usuario.getApellidoMaterno() + "\" con RUT " + usuario.getRut() + "-" + usuario.getDigito() + " se eliminó exitosamente.");
        } catch (NonexistentEntityException ne) {
            JsfUtil.addErrorMessage(ne.getLocalizedMessage());
            return relatedOrListOutcome();
        } catch (Exception e) {
            JsfUtil.ensureAddErrorMessage(e, "Ocurrió un error de persistencia. Por favor intente denuevo.");
            return null;
        }
        return relatedOrListOutcome();
    }

    public String search() {
        actualizarUsuarios(true);
        return "usuario_list";
    }

    private void actualizarUsuarios(Boolean reset) {
        String query = "select object(o) from Usuario as o where o.rut like :rut and o.digito like :digito and o.primerNombre like :primerNombre and o.apellidoMaterno like :apellidoMaterno and o.apellidoPaterno like :apellidoPaterno order by o.apellidoPaterno asc, o.primerNombre asc";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters = createParameters(usuarioSearch);
        usuarioItems = jpaController.findUsuarioEntities(jpaController.getUsuarioCount(), 0, query, parameters);
        //reset(reset, true);
        //getPagingInfo();
        //usuarioItems = usuarioTotal.subList(getPagingInfo().getFirstItem(), getPagingInfo().getLastItem());
    }

    private Map<String, Object> createParameters(Usuario user) {
        Map<String, Object> parameters = new HashMap<String, Object>();

        if (user != null) {
            if (!user.getRut().equals("")) {
                parameters.put("rut", "%" + user.getRut() + "%");
            } else {
                parameters.put("rut", "%");
            }
            if (user.getDigito() != null) {
                parameters.put("digito", user.getDigito().toString());
            } else {
                parameters.put("digito", "_");
            }
            if (!user.getPrimerNombre().equals("")) {
                parameters.put("primerNombre", "%" + user.getPrimerNombre() + "%");
            } else {
                parameters.put("primerNombre", "%");
            }
            if (!user.getApellidoPaterno().equals("")) {
                parameters.put("apellidoPaterno", "%" + user.getApellidoPaterno() + "%");
            } else {
                parameters.put("apellidoPaterno", "%");
            }
            if (!user.getApellidoMaterno().equals("")) {
                parameters.put("apellidoMaterno", "%" + user.getApellidoMaterno() + "%");
            } else {
                parameters.put("apellidoMaterno", "%");
            }
            return parameters;
        }
        return null;
    }

    private String relatedOrListOutcome() {
        String relatedControllerOutcome = relatedControllerOutcome();
        if (relatedControllerOutcome != null) {
            return relatedControllerOutcome;
        }
        return listSetup();
    }

    public List<Usuario> getUsuarioItems() {
        if (usuarioItems == null) {
            //usuarioItems = jpaController.findUsuarioEntities(pagingInfo.getBatchSize(), pagingInfo.getFirstItem());
            usuarioItems = jpaController.findUsuarioEntities();
        }
        Collections.sort(usuarioItems);
        return usuarioItems;
    }

    public String next() {
        reset(false);
        return "usuario_list";
    }

    public String prev() {
        reset(false);
        return "usuario_list";
    }

    private String relatedControllerOutcome() {
        String relatedControllerString = JsfUtil.getRequestParameter("jsfcrud.relatedController");
        String relatedControllerTypeString = JsfUtil.getRequestParameter("jsfcrud.relatedControllerType");
        if (relatedControllerString != null && relatedControllerTypeString != null) {
            FacesContext context = FacesContext.getCurrentInstance();
            Object relatedController = context.getApplication().getELResolver().getValue(context.getELContext(), null, relatedControllerString);
            try {
                Class<?> relatedControllerType = Class.forName(relatedControllerTypeString);
                Method detailSetupMethod = relatedControllerType.getMethod("detailSetup");
                return (String) detailSetupMethod.invoke(relatedController);
            } catch (ClassNotFoundException e) {
                throw new FacesException(e);
            } catch (NoSuchMethodException e) {
                throw new FacesException(e);
            } catch (IllegalAccessException e) {
                throw new FacesException(e);
            } catch (InvocationTargetException e) {
                throw new FacesException(e);
            }
        }
        return null;
    }

    private void reset(boolean resetFirstItem) {
        usuario = null;
        mostrarClave = false;
        usuarioItems = null;
        usuarioClave = null;
        if (resetFirstItem) {
            //pagingInfo.setFirstItem(0);
        }
    }

    public void validateCreate(FacesContext facesContext, UIComponent component, Object value) {
        Usuario newUsuario = new Usuario();
        String newUsuarioString = converter.getAsString(FacesContext.getCurrentInstance(), null, newUsuario);
        String usuarioString = converter.getAsString(FacesContext.getCurrentInstance(), null, usuario);
        if (!newUsuarioString.equals(usuarioString)) {
            createSetup();
        }
    }

    public void validateRut(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        boolean b = Pattern.matches("[0-9]*", value.toString());
        if (!b) {
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "El Rut ingresado no es válido.", null);
            context.addMessage(null, fm);
            throw new ValidatorException(fm);
        }
    }

    public void validateRutSearch(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        boolean b = Pattern.matches("[0-9]*", value.toString());
        if (!b) {
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "El Rut ingresado no es válido.", null);
            context.addMessage(null, fm);
            throw new ValidatorException(fm);
        }
    }

    public void validateDigito(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        boolean b = Pattern.matches("[0-9kK]{1}", value.toString());
        if (!b) {
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "El digito verificador ingresado no es válido.", null);
            context.addMessage(null, fm);
            throw new ValidatorException(fm);
        } else {
        }
    }

    public void validateNombre(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        boolean b = Pattern.matches("[\\p{L}\\p{M}*]{1,20}", value.toString());
        if (!b) {
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "El nombre \"" + value.toString() + "\" no es válido.", null);
            context.addMessage(null, fm);
            throw new ValidatorException(fm);
        }
    }

    public void validateClave(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        boolean b = value.toString().length() > 5 || value.toString().length() == 0;
        if (!b) {
            FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "La clave debe ser de mínimo 6 caracteres.", null);
            context.addMessage(null, fm);
            throw new ValidatorException(fm);
        }
    }

    public String login() {
        String clave = null, rut = null;
        char digito;
        SHA1 s = new SHA1();
        if (usuario == null) {
            JsfUtil.addErrorMessage("Ocurrió un error al intentar ingresar al sistema.");
            return null;
        }
        try {
            clave = s.getHash(usuario.getClave());
        } catch (Exception ex) {
            System.out.println("Error al crear hash de la clave." + ex.toString());
        }

        rut = usuario.getRut();
        digito = usuario.getDigito();
        String query = "select object(o) from Usuario as o where o.rut = :rut and o.digito = :digito";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("rut", rut);
        parameters.put("digito", digito);
        List<Usuario> usuarios = null;
        try {
            usuarios = jpaController.findUsuarioEntities(1, 0, query, parameters);
        } catch (Exception ex) {
            JsfUtil.ensureAddErrorMessage(ex, "Error:");
        }
        if (usuarios == null) {
            JsfUtil.addErrorMessage("No se pudo realizar la consulta, por favor intente denuevo.");
            return null;
        } else if (usuarios.size() == 0) {
            JsfUtil.addErrorMessage("El RUT y/o la clave no son válidos. Verifique los datos e intente de nuevo.");
            //FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, "*", null);
            //FacesContext.getCurrentInstance().addMessage("loginform:rut", fm);
            //FacesContext.getCurrentInstance().addMessage("loginform:digito", fm);
            return null;
        } else {
            query = "select object(o) from Usuario as o where o.rut = :rut and o.clave = :clave and o.digito = :digito";
            parameters.put("clave", clave);
            try {
                usuarios = jpaController.findUsuarioEntities(1, 0, query, parameters);
            } catch (Exception ex) {
                JsfUtil.ensureAddErrorMessage(ex, "Error:" + ex.toString());
            }
            if (usuarios != null && usuarios.size() > 0) {
                Usuario u = usuarios.get(0);
                Rol admin = new Rol();
                Rol user = new Rol();
                admin.setNombre("Administrador");
                user.setNombre("Usuario");
                if(u.getRolCollection().contains(admin) || u.getRolCollection().contains(user))
                {
                    FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(AuthenticationPhaseListener.AUTHENTICATED_CONSULTANT_KEY, u);
                    JsfUtil.addSuccessMessage("Nombre: " + u.getPrimerNombre() + " " + u.getApellidoPaterno() + " " + u.getApellidoMaterno() + ".");
                    JsfUtil.addSuccessMessage("Rut: " + u.getRut() + "-" + u.getDigito());
                    usuarioLogin.setRut(u.getRut());
                    usuarioLogin.setDigito(u.getDigito());
                    usuarioLogin.setPrimerNombre(u.getPrimerNombre());
                    usuarioLogin.setSegundoNombre(u.getSegundoNombre());
                    usuarioLogin.setApellidoPaterno(u.getApellidoPaterno());
                    usuarioLogin.setApellidoMaterno(u.getApellidoMaterno());
                    clearUsuario(usuario);
                    return "welcome";
                }else
                    JsfUtil.addErrorMessage("El usuario no pertenece a un rol con acceso al sistema.");
            } else {
                JsfUtil.addErrorMessage("El RUT y/o la clave no son válidos. Verifique los datos e intente de nuevo.");
           
            }
            return null;

        }
    }

    public String logout() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        if (session != null) {
            session.invalidate();
        }
        JsfUtil.addSuccessMessage("Has cerrado la sesión.");

        return "login";
    }

    public void clearUsuario(Usuario user) {
        if (user != null) {
            user.setRut("");
            user.setDigito(null);
            user.setPrimerNombre("");
            user.setSegundoNombre("");
            user.setApellidoPaterno("");
            user.setApellidoMaterno("");
            usuarioLogin.setClave("");
            usuarioClave.setClave("");
        }
    }

    public Converter getConverter() {
        return converter;
    }

    public Boolean getAdmin(){
        Usuario u = new Usuario();
        u = jpaController.findUsuario(usuarioLogin.getRut());
        Rol r = new Rol();
        r.setNombre("Administrador");
        return u.getRolCollection().contains(r);
    }

    public Boolean getUser(){
        Usuario u = new Usuario();
        u = jpaController.findUsuario(usuarioLogin.getRut());
        Rol r = new Rol();
        r.setNombre("Usuario");
        return u.getRolCollection().contains(r);

    }

}
